package de.westranger.forex.trading.genetic.allel.operator;

import java.lang.reflect.Array;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

import de.westranger.forex.trading.genetic.allel.Node;
import de.westranger.forex.trading.genetic.allel.chromosom.Chromosom;

public final class Util {
	public static final int QUERY_OPERATORS = 1;
	public static final int QUERY_TERMINALS = 2;
	
	public static synchronized Node cloneExpression(final Node node, final Node root){
		Node result = null;
		if(node != null){
			result = node.clone();

//			System.out.println("node: "+node.getClass().getSimpleName()+" clone:"+ result.getClass().getSimpleName());
//			if(node.getLeftChild() != null){
//				System.out.print("node l: "+node.getLeftChild().getClass().getSimpleName());
//			}
//			if(result.getLeftChild() != null){
//				System.out.print(" clone l:"+ result.getLeftChild().getClass().getSimpleName());				
//			}
//			System.out.println();
//			
//			if(node.getRightChild() != null){
//				System.out.print("node r: "+node.getRightChild().getClass().getSimpleName());
//			}
//			if(result.getRightChild() != null){
//				System.out.print(" clone r:"+ result.getRightChild().getClass().getSimpleName());
//			}
//			System.out.println();

			result.setRoot(root);
			result.setLeftChild(cloneExpression(node.getLeftChild(),result));
			result.setRightChild(cloneExpression(node.getRightChild(),result));
		}

//		if(root != null && node != null){
//			
//			System.out.println("root : " + root.getClass().getSimpleName() + " c:" + node.getClass().getSimpleName() + " l"+result.getClass().getSimpleName());
//		}
		
		return result;
	}
	
	public static synchronized List<Node> getQueryNodes(final Node node,final int queryType){
		final List<Node> list = new LinkedList<Node>();
		if(queryType == QUERY_OPERATORS){
			checkForOperator(node,list);
		}else{
			checkForTerminal(node,list);	
		}
		return list;
	}
	
	private static synchronized List<Node> traverseOperator(final Node node,final List<Node> list){
		checkForOperator(node.getLeftChild(),list);
		checkForOperator(node.getRightChild(),list);
		return list;
	}
	
	private static synchronized void checkForOperator(final Node node,final List<Node> list){
		if(node != null){
			if(node instanceof LogicalOperator){
				list.add(node);
				traverseOperator(node,list);
			}else if(node instanceof CompareOperator){
				list.add(node);
			}
		}
	}
	
	private static synchronized List<Node> traverseTerminals(final Node node,final List<Node> list){
		checkForTerminal(node.getLeftChild(),list);
		checkForTerminal(node.getRightChild(),list);
		return list;
	}
	
	private static synchronized void checkForTerminal(final Node node,final List<Node> list){
		if(node != null){
			if(node instanceof LogicalOperator || node instanceof CompareOperator){
				traverseTerminals(node,list);
			}else{
				list.add(node);	
			}
		}
	}
	
	public static synchronized void exchange(final Node nodeA, final Node nodeB){
		final Node rootA = nodeA.getRoot();
		final Node rootB = nodeB.getRoot();
		
		nodeA.setRoot(rootB);
		nodeB.setRoot(rootA);
		
		if(rootA != null){
			if(rootA.getLeftChild() == nodeA){
				rootA.setLeftChild(nodeB);
			}else{
				rootA.setRightChild(nodeB);
			}
		}
		
		if(rootB != null){
			if(rootB.getLeftChild() == nodeB){
				rootB.setLeftChild(nodeA);
			}else{
				rootB.setRightChild(nodeA);
			}
		}
	}

	public static synchronized double sum (final Chromosom[] population){
		double sum = 0.0;
		for (final Chromosom chrom : population) {
			sum += chrom.getFitness();
		}
		return sum;
	}

	public static synchronized void normalize(final Chromosom[] population){
		final double sum = sum(population);

		for(Chromosom c:population){
			c.setFitness(c.getFitness()/sum);
		}
	}
	
	public static synchronized boolean checkForInstanceShareing(final Node nodeA, final Node nodeB){
		boolean result = false;
		if(nodeA != null && nodeB != null){
			List<Node> queryNodesA = getQueryNodes(nodeA,Util.QUERY_OPERATORS);
			List<Node> queryNodesB = getQueryNodes(nodeB,Util.QUERY_OPERATORS);
			result = crossCheck(queryNodesA,queryNodesB);

			if(!result){
				result = crossCheck(queryNodesB,queryNodesA);
			}

			if(!result){
				queryNodesA = getQueryNodes(nodeA,Util.QUERY_TERMINALS);
				queryNodesB = getQueryNodes(nodeB,Util.QUERY_TERMINALS);
				result = crossCheck(queryNodesA,queryNodesB);
			}

			if(!result){
				result = crossCheck(queryNodesB,queryNodesA);
			}
		}
		return result;
	}

	private static synchronized boolean crossCheck(final List<Node> queryNodesA,final List<Node> queryNodesB){
		boolean result = false;
		for(Node x:queryNodesA){
			if(queryNodesB.contains(x)){
				result = true;
				break;
			}
		}
		return result;
	}
	
	public static synchronized <T> T[] removeFromArray(final T[] array,final T value){
		final Stack<T> stk = new Stack<T>();
		
		for(T tmp:array){
			stk.push(tmp);
		}
		stk.remove(value);
		
		@SuppressWarnings("unchecked")
		final T[] result = (T[]) Array.newInstance(value.getClass(), stk.size());
		stk.copyInto(result);
		return result;
	}
	
	// TODO testcase schreiben
	public static synchronized void parentCheck(final Node root,final Node tmp,final String exceptionString){
		List<Node> lst = Util.getQueryNodes(root, Util.QUERY_TERMINALS);

		for(Node n:lst){
			if(n instanceof Terminal){
				if(!(n.getRoot()instanceof CompareOperator)){
					throw new IllegalStateException(exceptionString + " node:"+n.toString());
				}
			}else if(n instanceof EvaluateTerminal){
				if(!(n.getRoot()instanceof LogicalOperator)){
					throw new IllegalStateException(exceptionString + " node:"+n.toString());	
				}
			}
		}
		
		lst = Util.getQueryNodes(root, Util.QUERY_OPERATORS);

		for(Node n:lst){
			if(n instanceof LogicalOperator){
				if(n.getLeftChild() instanceof Terminal){
					throw new IllegalStateException(exceptionString + " node:"+n.toString());
				}else if(n.getRightChild() instanceof Terminal){
					throw new IllegalStateException(exceptionString + " node:"+n.toString());
				}
			}else if(n instanceof CompareOperator){
				if(n.getLeftChild() instanceof EvaluateTerminal){
					throw new IllegalStateException(exceptionString + " node:"+n.toString());
				}else if(n.getRightChild() instanceof EvaluateTerminal){
					throw new IllegalStateException(exceptionString + " node:"+n.toString());
				}
			}			
		}
	}
	
}
